// Chart.cpp : implementation file
//

#include "stdafx.h"
#include <cmath>
#include "ASUI.h"
#include "Chart.h"
#include "ITime.h"
#include "Time.h"
#include "IReading.h"


// Chart

IMPLEMENT_DYNAMIC(Chart, CPane)

Chart::Chart()
{

}

Chart::~Chart()
{
}


BEGIN_MESSAGE_MAP(Chart, CPane)
	ON_WM_PAINT()
END_MESSAGE_MAP()



// Chart message handlers

void Chart::OnPaint() 
{
	//CRect boundsRect;
	//GetClientRect(boundsRect);

	//CPaintDC dc(this);
	//dc.SetBoundsRect(boundsRect, DCB_DISABLE); // Make sure we stay within the panel
	//dc.SetBkMode(TRANSPARENT);
	//
	//// repaint the window with solid white background
	//CBrush brush;
	//brush.CreateSolidBrush(RGB(255,255,255));
	//dc.FillRect(boundsRect, &brush);

	//long int startSecond = startTime->getUnixTime();
	//long int endSecond = endTime->getUnixTime();
	//long int diffSeconds = endSecond - startSecond;
	//int steps = 5;
	//long int step = diffSeconds / steps;

	//dc.MoveTo(0, 0);

	//long int curSecond = startSecond;
	//for (int i=0; i<steps; i++) {
	//	double average = readingInformation->getReadingAverage(sensorId, readingType, Time((long int) (curSecond - 0.5*step)), Time((long int) (curSecond + 0.5*step)));
	//	TRACE("average=%f\n", average);
	//	int x, y;
	//	x = ((double) curSecond  - startSecond)/ diffSeconds * boundsRect.Width();
	//	y = (average / 100.0) * boundsRect.Height();
	//	//dc.LineTo(x, y);
	//	curSecond += step;
	//}

	CRect rect;
	GetClientRect(rect);

    CPaintDC dc(this);
    CDC dcMem;
	dcMem.CreateCompatibleDC(&dc);
    CBitmap bm;
	bm.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
    CBitmap * old = dcMem.SelectObject(&bm);    

	dcMem.SetBoundsRect(rect, DCB_DISABLE); // Make sure we stay within the panel
	dcMem.SetBkMode(TRANSPARENT);

	CBrush brush;
	// repaint the window with solid white background
	brush.CreateSolidBrush(RGB(255,255,255));
	dcMem.FillRect(rect, &brush);

	CPen thick_pen(PS_SOLID, 1.5, RGB(255,0,0));
	CPen* oldPen = dcMem.SelectObject(&thick_pen);	

	Time startTimeExtra((long int) (startTime->getUnixTime() + startTime->getDifferenceInSeconds(*endTime.get()) * 0.0));
	Time endTimeExtra((long int) (endTime->getUnixTime() - startTime->getDifferenceInSeconds(*endTime.get()) * 0.1));

	//clock_t timer = clock();
	std::list<unique_ptr<Sensor::IReading>> readings = readingInformation->getReading(sensorId, readingType, startTimeExtra, endTimeExtra);
	//if (readings.size() < 3) {
	//	return;
	//}
	//CString str;
	//str.Format("%f", double( clock() - timer ) / (double)CLOCKS_PER_SEC);
	//AfxMessageBox(str);

	long int startSecond = startTime->getUnixTime();
	long int endSecond = endTime->getUnixTime();

	std::list<unique_ptr<Sensor::IReading>>::iterator readingIter;

	double minValue = readings.front()->getValue();
	double maxValue = minValue;
	for (readingIter = readings.begin(); readingIter != readings.end(); readingIter++) {
		double value = (*readingIter)->getValue();
		if (value < minValue) {
			minValue = value;
		} else if (value > maxValue) {
			maxValue = value;
		}
	}

	// FIXME: use this to draw the graph too!
	double valueDifference = maxValue - minValue;
	double unitHeight = (int) std::pow(10, std::abs(std::floor(std::log10(valueDifference))));
	double adjustedTop = std::ceil(maxValue / unitHeight) * unitHeight;
	double adjustedBottom = std::floor(minValue / unitHeight) * unitHeight;
	double adjustedHeight = adjustedTop - adjustedBottom;
	double units = adjustedHeight / unitHeight;
	if (units < 3) {
		unitHeight *= 0.25;
	} else if (units < 6) {
		unitHeight *= 0.5;
	} else if (units > 8) {
		unitHeight *= 2;
	}
	TRACE("unitHeight=%d\n", unitHeight);
	TRACE("adjustedHeight=%d\n", adjustedHeight);


	CRect textRect;
	textRect.top = 0;
	textRect.bottom = 100;
	textRect.left = 0;
	textRect.right = 50;

	double verticalScale = adjustedHeight / (double) rect.Height();

	int value = std::ceil(maxValue / unitHeight) * unitHeight;
	int y = unitHeight / verticalScale;//0;
	int labelCount = std::floor(adjustedHeight / (double) unitHeight + 1);
	for (int i=1; i<labelCount-1; i++) {
		CPen guidePen(PS_DOT, 1, RGB(200,200,200));
		CPen* oldPen = dcMem.SelectObject(&guidePen);	
		dcMem.MoveTo(0, y);
		dcMem.LineTo(rect.Width(), y);
		dcMem.SelectObject(oldPen);

		textRect.top = y;
		y += unitHeight / verticalScale;
		textRect.bottom = y;
		
		TRACE("top=%d bottom=%d\n", textRect.top, textRect.bottom);
		CString titleString;
		titleString.Format("%d", value);
		dcMem.DrawTextA(titleString, -1, textRect, DT_CENTER);
		TRACE("y=%d\n", y);

		value -= unitHeight;
	}


	textRect.top = rect.Height() - 20;
	textRect.bottom = rect.Height();
	textRect.left = 0;
	textRect.right = 50;

	Time adjustedStartTime((long int) (std::floorl(startTime->getUnixTime() / 86400l) * 86400l));
	Time adjustedEndTime((long int) (std::ceill(endTime->getUnixTime() / 86400l) * 86400l));
	long int adjustedSecondDifference = adjustedEndTime.getUnixTime() - adjustedStartTime.getUnixTime();
	long int timeUnit = 86400;
	long int labelSecond = adjustedStartTime.getUnixTime();
	int timeLabelCount = adjustedSecondDifference / timeUnit + 1;
	while (timeLabelCount > 6) {
		timeUnit *= 2;
		timeLabelCount = adjustedSecondDifference / timeUnit + 1;
	}
	double secondToHorz = rect.Width() / (double) adjustedSecondDifference;
	int timeLabelStep = timeUnit * secondToHorz;
	for (int i=0; i<timeLabelCount; i++) {
		Time labelTime(labelSecond);

		textRect.left = ((i - 0.5) * timeUnit) * secondToHorz + 50;
		textRect.right = ((i + 0.5) * timeUnit) * secondToHorz + 50;
		if (textRect.right < rect.Width()) {
			CString titleString;
			titleString.Format("%02d-%02d", labelTime.getMonth(), labelTime.getDay());
			dcMem.DrawTextA(titleString, -1, textRect, DT_CENTER);

			labelSecond += timeUnit;
		}
	}

	
	TRACE("Reading Count = %d\n", readings.size());
	dcMem.MoveTo(((double)(readings.front()->getTime()->getUnixTime() - startSecond + 50))/(endSecond - startSecond)*rect.Width() + 50, (readings.front()->getValue() - adjustedBottom)/adjustedHeight *rect.Height());
	for (readingIter = readings.begin(); readingIter != readings.end(); readingIter++) {
		long int readingSecond = (*readingIter)->getTime()->getUnixTime();
		
		TRACE("Rect width = %d, height = %d, x = %f\n", rect.Width(), rect.Height(), ((double)readingSecond - startSecond)/(endSecond - startSecond)*rect.Width());
		TRACE("top=%d left=%d right=%d bottom=%d\n", rect.top, rect.left, rect.right, rect.bottom);

		TRACE(
			"%d %d %f %f\n",
			readingSecond - startSecond,
			endSecond - startSecond,
			((double)(readingSecond - startSecond))/(endSecond - startSecond),
			((double)(readingSecond - startSecond))/(endSecond - startSecond)*rect.Width()
			);

		dcMem.LineTo(((double)(readingSecond - startSecond))/(endSecond - startSecond + 50)*rect.Width() + 50, ((*readingIter)->getValue() - adjustedBottom)/adjustedHeight *rect.Height());
	}


	dcMem.SelectObject(oldPen);

	dc.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &dcMem, 0, 0, SRCCOPY);
}

